/*
 * Created by hql on 2024-08-22 14:44:41
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;

namespace IMMotors
{
    public class AAMgr : MonoSingleton<AAMgr>
    {
        #region 资源跟踪

        private readonly Dictionary<string, AsyncOperationHandle> _assetHandles = new();
        private readonly Dictionary<string, List<GameObject>> _instantiatedObjects = new();
        private AsyncOperationHandle<SceneInstance>? _pendingHdl;
        private bool _loadSceneInterrupted = false;
        #endregion

        #region 初始化

        private void Start()
        {
            Addressables.InitializeAsync();
        }

        private void OnDestroy()
        {
            ClearAll();
        }

        #endregion

        #region 基础加载方法

        /// <summary>
        /// 通过Key加载资源（泛型）
        /// </summary>
        public async Task<T> LoadAssetAsync<T>(string key) where T : class
        {
            try
            {
                if (_assetHandles.TryGetValue(key, out var existingHandle))
                {
                    return existingHandle.Result as T;
                }

                var handle = Addressables.LoadAssetAsync<T>(key);
                _assetHandles[key] = handle;
                await handle.Task;

                if (handle.Status == AsyncOperationStatus.Succeeded)
                {
                    return handle.Result;
                }

                throw new Exception($"Failed to load asset {key}: {handle.OperationException}");
            }
            catch (Exception e)
            {
                Debug.LogError($"LoadAssetAsync error: {e.Message}");
                throw;
            }
        }

        /// <summary>
        /// 通过Key实例化对象
        /// </summary>
        public async Task<GameObject> InstantiateAsync(string key, Transform parent = null)
        {
            GameObject obj = await LoadAssetAsync<GameObject>(key);
            return Instantiate(obj, parent);
        }

        #endregion

        #region 高级加载方法

        /// <summary>
        /// 通过Label加载多个资源
        /// </summary>
        public async Task<List<T>> LoadAssetsByLabelAsync<T>(string label, Action<T> callback = null)
        {
            var results = new List<T>();
            var handle = Addressables.LoadAssetsAsync<T>(label, asset =>
            {
                results.Add(asset);
                callback?.Invoke(asset);
            });

            await handle.Task;

            if (handle.Status == AsyncOperationStatus.Succeeded)
            {
                // 记录handle但不按key记录，因为label可能对应多个资源
                return results;
            }

            throw new Exception($"Failed to load assets by label {label}: {handle.OperationException}");
        }

        /// <summary>
        /// 通过Label和Key加载特定资源
        /// </summary>
        public async Task<T> LoadAssetByLabelAndKeyAsync<T>(string label, string key) where T : class
        {
            var locations = await GetLocationsByLabelAsync(label);
            var targetLocation = locations.FirstOrDefault(l => l.PrimaryKey == key);

            if (targetLocation == null)
            {
                throw new Exception($"No asset found with label {label} and key {key}");
            }

            var loadHandle = Addressables.LoadAssetAsync<T>(targetLocation);
            await loadHandle.Task;

            if (loadHandle.Status == AsyncOperationStatus.Succeeded)
            {
                return loadHandle.Result;
            }
            else
            {
                throw new Exception($"Failed to load asset: {loadHandle.OperationException}");
            }
        }

        /// <summary>
        /// 获取Label对应的所有资源位置
        /// </summary>
        private async Task<List<IResourceLocation>> GetLocationsByLabelAsync(string label)
        {
            var handle = Addressables.LoadResourceLocationsAsync(label);
            await handle.Task;

            if (handle.Status == AsyncOperationStatus.Succeeded)
            {
                return handle.Result.ToList();
            }

            throw new Exception($"Failed to load locations for label {label}: {handle.OperationException}");
        }

        /// <summary>
        /// 获取Key对应的资源位置
        /// </summary>
        private async Task<IResourceLocation> GetLocationByKeyAsync(string key)
        {
            var handle = Addressables.LoadResourceLocationsAsync(key);
            await handle.Task;

            if (handle.Status == AsyncOperationStatus.Succeeded)
            {
                return handle.Result.ToList().FirstOrDefault(location => location.PrimaryKey == key);
            }

            throw new Exception($"Failed to load location for key {key}: {handle.OperationException}");
        }

        #endregion

        #region 资源释放

        /// <summary>
        /// 释放单个资源
        /// </summary>
        public void ReleaseAsset(string key)
        {
            if (_assetHandles.TryGetValue(key, out var handle))
            {
                Addressables.Release(handle);
                _assetHandles.Remove(key);
            }
        }

        /// <summary>
        /// 释放实例化的对象
        /// </summary>
        public void ReleaseInstance(GameObject instance)
        {
            if (instance == null) return;

            // 查找这个实例对应的key
            string foundKey = null;
            foreach (var kvp in _instantiatedObjects)
            {
                if (kvp.Value.Contains(instance))
                {
                    foundKey = kvp.Key;
                    kvp.Value.Remove(instance);
                    break;
                }
            }

            if (foundKey != null)
            {
                Addressables.ReleaseInstance(instance);

                // 如果该key没有其他实例了，清理列表
                if (_instantiatedObjects[foundKey].Count == 0)
                {
                    _instantiatedObjects.Remove(foundKey);
                }
            }
            else
            {
                Debug.LogWarning("Instance was not tracked by AddressablesManager");
                Destroy(instance);
            }
        }

        /// <summary>
        /// 释放所有资源
        /// </summary>
        public void ClearAll()
        {
            // 释放所有实例化对象
            foreach (var kvp in _instantiatedObjects)
            {
                foreach (var obj in kvp.Value)
                {
                    if (obj != null)
                    {
                        Addressables.ReleaseInstance(obj);
                    }
                }
            }

            _instantiatedObjects.Clear();

            // 释放所有资源句柄
            foreach (var handle in _assetHandles.Values)
            {
                Addressables.Release(handle);
            }

            _assetHandles.Clear();
        }

        #endregion

        #region 状态检查

        /// <summary>
        /// 检查资源是否已加载
        /// </summary>
        public bool IsAssetLoaded(string key)
        {
            return _assetHandles.ContainsKey(key) &&
                   _assetHandles[key].IsDone &&
                   _assetHandles[key].Status == AsyncOperationStatus.Succeeded;
        }

        /// <summary>
        /// 获取资源加载进度
        /// </summary>
        public float GetLoadProgress(string key)
        {
            if (_assetHandles.TryGetValue(key, out var handle))
            {
                return handle.PercentComplete;
            }

            return 0f;
        }

        /// <summary>
        /// 获取下载大小（字节）
        /// </summary>
        public async Task<long> GetDownloadSizeAsync(string key)
        {
            var sizeHandle = Addressables.GetDownloadSizeAsync(key);
            await sizeHandle.Task;

            if (sizeHandle.Status == AsyncOperationStatus.Succeeded)
            {
                return sizeHandle.Result;
            }

            throw new Exception($"Failed to get download size for {key}: {sizeHandle.OperationException}");
        }

        #endregion

        #region 场景加载

        /// <summary>
        /// 加载Addressables场景
        /// </summary>
        public async Task<SceneInstance> LoadSceneAsync(string sceneKey, bool activateOnLoad = true,
            UnityEngine.SceneManagement.LoadSceneMode loadMode = UnityEngine.SceneManagement.LoadSceneMode.Single)
        {
            try
            {
                _loadSceneInterrupted = false;
                var handle = Addressables.LoadSceneAsync(sceneKey, loadMode, activateOnLoad);
                _pendingHdl = handle;
                await handle.Task;
                if (_loadSceneInterrupted)
                {
                    Debug.Log("LoadSceneAsync Interrupted");
                    return new SceneInstance();
                }
                if (handle.Status == AsyncOperationStatus.Succeeded)
                {
                    return handle.Result;
                }

                throw new Exception($"Failed to load scene {sceneKey}: {handle.OperationException}");
            }
            catch (Exception e)
            {
                Debug.LogError($"LoadSceneAsync error: {e.Message}");
                throw;
            }
        }
        public void CancelSceneLoad()
        {
            if (_pendingHdl.HasValue)
            {
                Addressables.Release(_pendingHdl.Value);
                _pendingHdl = null;
                _loadSceneInterrupted = true;
            }
        }
        /// <summary>
        /// 卸载场景
        /// </summary>
        public async Task UnloadSceneAsync(SceneInstance sceneInstance)
        {
            try
            {
                var handle = Addressables.UnloadSceneAsync(sceneInstance);
                await handle.Task;

                if (handle.Status != AsyncOperationStatus.Succeeded)
                {
                    throw new Exception($"Failed to unload scene: {handle.OperationException}");
                }
            }
            catch (Exception e)
            {
                Debug.LogError($"UnloadSceneAsync error: {e.Message}");
                throw;
            }
        }

        #endregion
    }
}
